#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <err.h>

#define DEFAULTPORT 3337
#define INITIAL_SIZE 1024

struct Buffer {
    char* data;
    int next;
    size_t size;
};

struct Buffer* new_buffer();
void reserve_space(struct Buffer* b, size_t bytes);
void serialize_str(char* x, struct Buffer* b);
char* loadfile();

int main()
{
    char response[] = "HTTP/1.1 200 OK\r\n";
    char header[]   = "Content-Type: text/html; charset=UTF-8\r\n\r\n";
    char* body;
	int one = 1, client_fd;
	struct sockaddr_in svr_addr, cli_addr;
	socklen_t sin_len = sizeof(cli_addr);
	int bytes_sent, bytes_recv;
    struct Buffer* buf = new_buffer();
    
	int sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock < 0)
		err(1, "can't open socket");
 
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
 
	int port = DEFAULTPORT;
	svr_addr.sin_family = AF_INET;
	svr_addr.sin_addr.s_addr = INADDR_ANY;
	svr_addr.sin_port = htons(port);
 
	if (bind(sock, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1) {
		close(sock);
		err(1, "Can't bind");
	}

	listen(sock, 5);
	while (1) {
		if ((client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len)) == -1) {
			perror("Can't accept");
			continue;
		}
		
		// receive request
		if (bytes_recv = recv(client_fd,buf->data, buf->size-1, 0 ) == -1){
		    perror("recv");
		    continue;
		}
		printf("%s\n",buf->data);
		free(buf);
		buf = new_buffer();
 
        // send response
        body = loadfile();
        serialize_str(response,buf);
        serialize_str(header,buf);
        serialize_str(body,buf);
		if (bytes_sent = send(client_fd, buf->data, buf->size, 0) == -1){
		    perror("send");
		    continue;
		} /*-1:'\0'*/
		free(buf);
		free(body);
		buf = new_buffer();
		close(client_fd);
	}
	return 0;
	
}

struct Buffer* new_buffer() {
    struct Buffer *b = malloc(sizeof(struct Buffer));
 
    b->data = malloc(INITIAL_SIZE);
    b->size = INITIAL_SIZE;
    b->next = 0;

    return b;
};

void reserve_space(struct Buffer* b, size_t bytes) {
	while((b->next + bytes) > b->size) {
		b->data = realloc(b->data,b->size * 2);
        b->size *= 2;
	}
}

void serialize_str(char* x, struct Buffer* b) {
    reserve_space(b,strlen(x));
    memcpy(((char *)b->data) + b->next,x,strlen(x));
    b->next += (int)strlen(x);
}

char* loadfile() {
    FILE *fd;
    long sz;
    char *buffer;
    
    if ((fd = fopen("index.html","rb")) == NULL) {
        perror("opening index.html");
        exit(1);
    }
    fseek(fd, 0L, SEEK_END);
    sz = ftell(fd);
    fseek(fd, 0L, SEEK_SET);
    
    if ((buffer = malloc(sz*sizeof(char))) == NULL) {
        fclose(fd);
        fputs("memory alloc fails",stderr);
        exit(1);
    }
    
    if (1 != fread(buffer, sz, 1, fd)) {
        fclose(fd);
        free(buffer);
        fputs("entire read fails",stderr);
        exit(1);
    }
    strcat(buffer,"\r\n");
    fclose(fd);
    return buffer;
}
